package com.ly.fn.biz.ins.srv.api.register;

import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @Author xiaoyuan.wang
 * @Date 2021/9/27 10:44
 * @Version 1.0
 */
public class InsServiceClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {


//  ？：匹配文件名中的一个字符；
//  * ：匹配文件名中的任意个字符
//  **：匹配多层路径

    private static final String DEFAULT_RESOURCE_PATTERN = "**/**Service*.class"; //"**/**ServiceImpl*.class";


    private String resourcePattern = DEFAULT_RESOURCE_PATTERN;

    private String termResourcePattern = "**/**Term.class";

    private ResourcePatternResolver resourcePatternResolver;

    private MetadataReaderFactory metadataReaderFactory;

    public InsServiceClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) {
        super(registry, false, environment, resourceLoader);
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    }

    public Set<Class> findFacadeService(String basePackage) {
        Set<Class> exportServiceSet = new LinkedHashSet<>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + '/' + this.resourcePattern;
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    try {
                        MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                        Class clz = getResourceLoader().getClassLoader().loadClass(metadataReader.getClassMetadata().getClassName());
                        if (AnnotationUtils.getAnnotation(clz, Service.class) != null ||
                                AnnotationUtils.getAnnotation(clz, Component.class) != null) {
                            exportServiceSet.add(clz);
                        }
                    } catch (Throwable ex) {
                        throw new BeanDefinitionStoreException(
                                "Failed to read candidate component class: " + resource, ex);
                    }
                }
            }
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
        }
        return exportServiceSet;
    }

    public Set<Class> findTermService(String basePackage) {
        Set<Class> exportTermSet = new LinkedHashSet<>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + '/' + this.termResourcePattern;
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    try {
                        MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                        Class clz = getResourceLoader().getClassLoader().loadClass(metadataReader.getClassMetadata().getClassName());
                        if (AnnotationUtils.getAnnotation(clz, Service.class) != null ||
                                AnnotationUtils.getAnnotation(clz, Component.class) != null) {
                            exportTermSet.add(clz);
                        }
                    } catch (Throwable ex) {
                        throw new BeanDefinitionStoreException(
                                "Failed to read candidate component class: " + resource, ex);
                    }
                }
            }
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
        }
        return exportTermSet;
    }
}
